﻿using System.Linq;
using UnityEngine.Pool;

namespace System.Collections.Generic {

    internal sealed class KHBD_SortedDictionary<TKey, TValue> : KHBD_Object, IDictionary<TKey, TValue> where TValue : class {

        public static readonly ObjectPool<KHBD_SortedDictionary<TKey, TValue>> Pool = new(() => new(true),
            actionOnGet: item => item.Initialize(), actionOnRelease: item => item.Dispose(), maxSize: 1024 * 1024, collectionCheck: false);

        private static readonly ObjectPool<LinkedList<TKey>> LinkedListPool = new(() => new(), actionOnRelease: item => item.Clear(), maxSize: 1024 * 1024, collectionCheck: false);


        // 实现会消耗不必要的性能开销
        ICollection<TKey> IDictionary<TKey, TValue>.Keys => throw new System.NotSupportedException();
        ICollection<TValue> IDictionary<TKey, TValue>.Values => throw new System.NotSupportedException();

        private Dictionary<TKey, TValue> m_InnerDictionary;
        private Dictionary<TKey, LinkedListNode<TKey>> m_NodeDictionary;
        private LinkedList<TKey> m_KeyList;

        public int Count => m_KeyList.Count;

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;

        public KHBD_SortedDictionary() : base() {
            Initialize();
        }

        private KHBD_SortedDictionary(bool _) : base() {
        }

        private void Initialize() {
            m_InnerDictionary = DictionaryPool<TKey, TValue>.Get();
            m_NodeDictionary = DictionaryPool<TKey, LinkedListNode<TKey>>.Get();
            m_KeyList = LinkedListPool.Get();
        }

        protected override void Dispose() {
            DictionaryPool<TKey, TValue>.Release(m_InnerDictionary);
            m_InnerDictionary = null;
            DictionaryPool<TKey, LinkedListNode<TKey>>.Release(m_NodeDictionary);
            m_NodeDictionary = null;
            LinkedListPool.Release(m_KeyList);
            m_KeyList = null;
        }

        public void Clear() {
            m_InnerDictionary.Clear();
            m_NodeDictionary.Clear();
            m_KeyList.Clear();
        }

        public bool ContainsKey(TKey key) => m_InnerDictionary.ContainsKey(key);

        public bool Contains(KeyValuePair<TKey, TValue> item) => m_InnerDictionary.Contains(item);

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
            foreach (var key in m_KeyList) {
                yield return new KeyValuePair<TKey, TValue>(key, m_InnerDictionary[key]);
            }
        }

        public bool TryGetValue(TKey key, out TValue value) => m_InnerDictionary.TryGetValue(key, out value);

        public TValue this[TKey key] {
            get => m_InnerDictionary[key];
            set {
                if (m_InnerDictionary.TryGetValue(key, out var oldValue)) {
                    if (oldValue?.Equals(value) ?? (value != null)) {
                        DoRemove(key);
                        Add(key, value);
                    }
                } else {
                    Add(key, value);
                }
            }
        }

        public void Add(TKey key, TValue value) {
            if (m_InnerDictionary.ContainsKey(key)) {
                throw new System.ArgumentException("An item with the same key has already been added.");
            }

            var node = m_KeyList.AddLast(key);
            m_NodeDictionary.Add(key, node);
            m_InnerDictionary.Add(key, value);
        }

        public void Add(KeyValuePair<TKey, TValue> item) {
            var (key, value) = item;
            Add(key, value);
        }

        public bool Remove(TKey key) {
            if (m_InnerDictionary.TryGetValue(key, out var _)) {
                DoRemove(key);
                return true;
            }
            return false;
        }

        public bool Remove(KeyValuePair<TKey, TValue> item) {
            var (key, value) = item;
            if (m_InnerDictionary.TryGetValue(key, out var oldValue) && (oldValue?.Equals(value) ?? (value == null))) {
                DoRemove(key);
                return true;
            }
            return false;
        }

        private void DoRemove(TKey key) {
            var node = m_NodeDictionary[key];
            m_KeyList.Remove(node);
            m_NodeDictionary.Remove(key);
            m_InnerDictionary.Remove(key);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
            UnityEngine.Assertions.Assert.IsNotNull(array);

            var i = 0;
            foreach (var kv in this) {
                array[arrayIndex + i++] = kv;
            }
        }
    }
}
